#include "CuVariant.h"

__device__ CuVariant::CuVariant()
{
	m_type = Type::none;
}

__device__ CuVariant::CuVariant(const CuVariant& other)
	:CuVariant()
{
	*this = other;
}

__device__ CuVariant::~CuVariant()
{
	destroy();
}

__device__ void CuVariant::operator=(const CuVariant& other)
{
	if (this == &other)
		return;

	create(other.m_type);
	if (m_type == Type::boolean)
		*(Boolean*)m_data = *((Boolean*)other.m_data);
	else if (typeIsInteger(m_type))
		*(Integer*)m_data = *((Integer*)other.m_data);
	else if (m_type == Type::field)
		*(Field*)m_data = *((Field*)other.m_data);
}

__device__ void CuVariant::create(int type)
{
	if (m_data)
		destroy();

	m_type = type;
	if (m_type == Type::boolean)
		m_data = new Boolean();
	else if (typeIsInteger(m_type))
		m_data = new Integer();
	else if (m_type == Type::field)
		m_data = new Field();
}

__device__ void CuVariant::destroy()
{
	if (m_data)
	{
		if (m_type == Type::boolean)
			delete (Boolean*)m_data;
		else if (typeIsInteger(m_type))
			delete (Integer*)m_data;
		else if (m_type == Type::field)
			delete (Field*)m_data;

		m_data = nullptr;
	}
}

__device__ int CuVariant::typeId() const
{
	return m_type;
}

__device__ CuVariant CuVariant::fromBoolean(const Boolean& t)
{
	CuVariant v;
	v.create(Type::boolean);
	*(Boolean*)v.m_data = t;
	return v;
}

__device__ Boolean CuVariant::toBoolean() const
{
	assert(m_type == Type::boolean);
	return *(Boolean*)m_data;
}

__device__ CuVariant CuVariant::fromInteger(const Integer& t)
{
	CuVariant v;
	v.create(t.type());
	*(Integer*)v.m_data = t;
	return v;
}

__device__ Integer CuVariant::toInteger() const
{
	assert(typeIsInteger(m_type));
	return *(Integer*)m_data;
}

__device__ CuVariant CuVariant::fromField(const Field& t)
{
	CuVariant v;
	v.create(Type::field);
	*(Field*)v.m_data = t;
	return v;
}

__device__ Field CuVariant::toField() const
{
	assert(m_type == Type::field);
	return *(Field*)m_data;
}

__device__ CuDebug operator<<(CuDebug dbg, const CuVariant& v)
{
	if (v.typeId() == Type::boolean)
		dbg << v.toBoolean();
	else if (typeIsInteger(v.typeId()))
		dbg << v.toInteger();
	else if (v.typeId() == Type::field)
		dbg << v.toField();

	return dbg;
}

__device__ Field variantToField(const CuVariant& v)
{
	int v_type = v.typeId();
	if (v_type == Type::boolean)
	{
		return booleanToField(v.toBoolean());
	}
	else if (typeIsInteger(v_type))
	{
		return v.toInteger().toField();
	}
	else if (v_type == Type::field)
	{
		return v.toField();
	}

	assert(0);
	return Field::zero();
}

__device__ int variantType(const CuVariant& v)
{
	return v.typeId();
}

__device__ Mode variantMode(const CuVariant & v)
{
	int v_type = v.typeId();
	if (v_type == Type::boolean)
	{
		return v.toBoolean().mode;
	}
	else if (typeIsInteger(v_type))
	{
		return v.toInteger().mode();
	}
	else if (v_type == Type::field)
	{
		return v.toField().mode;
	}

	assert(0);
	return Constant;
}

__device__ CuVariant variantFromString(Mode mode, const CuString& v)
{
	if (v == "true" || v == "false")
	{
		return CuVariant::fromBoolean(Boolean::create(mode, v == "true"));
	}
	else
	{
		int type = 0;
		CuString value;
		for(int i = boolean; i <= field; i++)
		{
			CuString imm_type = typeName(i);
			if (v.endsWith(imm_type))
			{
				value = v.left(v.size() - imm_type.size());
				type = i;
				break;
			}
		}

		if (typeIsInteger(type))
		{
			auto i = Integer::create(type, mode, cpp_int::fromString(value));
			return CuVariant::fromInteger(i);
		}
		else if (type == Type::field)
		{
			Field f;
			f.mode = mode;
			f.b = uint256_t::fromString(value);
			return CuVariant::fromField(f);
		}
		else
		{
			assert(0);
		}
	}

	assert(0);
	return CuVariant();
}

CuString variantToString(const CuVariant& v)
{
	int v_type = v.typeId();
	if (v_type == Type::boolean)
	{
		return v.toBoolean().toString();
	}
	else if (typeIsInteger(v_type))
	{
		return v.toInteger().toString();
	}
	else if (v_type == Type::field)
	{
		return v.toField().toString();
	}

	assert(0);
	return CuString();
}

__device__ BitArray variantToBitsLe(const CuVariant& v)
{
	int v_type = v.typeId();
	if (v_type == Type::boolean)
	{
		BitArray ret;
		ret.push_back(v.toBoolean());
		return ret;
	}
	else if (typeIsInteger(v_type))
	{
		return v.toInteger().toBitsLe();
	}
	else if (v_type == Type::field)
	{
		return v.toField().toBitsLe();
	}

	assert(0);
	return BitArray();
}

__device__ CuVariant variantFromBitsLe(int v_type, const BitArray& bits)
{
	if (typeIsInteger(v_type))
	{
		return CuVariant::fromInteger(Integer::fromBitsLe(v_type, bits));
	}
	else if (v_type == Type::field)
	{
		return CuVariant::fromField(Field::fromBitsLe(bits));
	}

	assert(0);
	return CuVariant();
}